home *** CD-ROM | disk | FTP | other *** search
/ Aminet 40 / Aminet 40 (2000)(Schatztruhe)[!][Dec 2000].iso / Aminet / misc / emu / msh-156.lha / han / hanfile.c < prev    next >
C/C++ Source or Header  |  1996-12-22  |  25KB  |  1,032 lines

  1. /*-
  2.  * $Id: hanfile.c,v 1.56 1996/12/22 00:22:33 Rhialto Rel $
  3.  * $Log: hanfile.c,v $
  4.  * Revision 1.56  1996/12/22  00:22:33  Rhialto
  5.  * Also update file lock (dir entry) on filw write error.
  6.  * And file write success.
  7.  *
  8.  * Revision 1.55  1993/12/30  23:28:00    Rhialto
  9.  * Freeze for MAXON5.
  10.  * Check Write() size to see it it will all fit.
  11.  * Optionally, update time of file on creation only.
  12.  * Lots of small changes for LONGNAMES option.
  13.  *
  14.  * Revision 1.54  1993/06/24  05:12:49    Rhialto
  15.  * MSCreateDir gave you an exclusive lock contrary to our liberal policy.
  16.  * DICE 2.07.54R.
  17.  *
  18.  * Revision 1.53  92/10/25  02:29:23  Rhialto
  19.  * Default conversion settable.
  20.  * OFFSET_END seeks were done in the wrong direction ARGH!
  21.  * Protect seek pos from being past EOF (due to SetFileSize).
  22.  * Count free clusters instead of free sectors.
  23.  *
  24.  * Revision 1.51  92/04/17  15:37:53  Rhialto
  25.  * Freeze for MAXON.
  26.  *
  27.  * Revision 1.46  91/10/06  18:24:36  Rhialto
  28.  *
  29.  * Freeze for MAXON
  30.  *
  31.  * Revision 1.45  91/10/03  23:36:16  Rhialto
  32.  * Implement in-situ conversions during Read()/Write()
  33.  *
  34.  * Revision 1.43  91/09/28  01:45:39  Rhialto
  35.  * Changed to newer syslog stuff.
  36.  *
  37.  * Revision 1.42  91/06/13  23:56:14  Rhialto
  38.  * DICE conversion
  39.  *
  40.  * Revision 1.40  91/03/03  17:53:53  Rhialto
  41.  * Freeze for MAXON
  42.  *
  43.  * Revision 1.32  90/11/23  23:54:57  Rhialto
  44.  * Prepare for syslog
  45.  *
  46.  * Revision 1.31  90/11/10  02:50:04  Rhialto
  47.  * Patch 3a. Update modification time of directories.
  48.  *
  49.  * Revision 1.30  90/06/04  23:17:33  Rhialto
  50.  * Release 1 Patch 3
  51.  *
  52.  * HANFILE.C
  53.  *
  54.  * The code for the messydos file system handler.
  55.  *
  56.  * This parts handles files and the File Allocation Table.
  57.  *
  58.  * This code is (C) Copyright 1989-1993 by Olaf Seibert. All rights reserved.
  59.  * May not be used or copied without a licence.
  60. -*/
  61.  
  62. #include "han.h"
  63. #include "dos.h"
  64. #include <string.h>
  65.  
  66. #if CONVERSIONS
  67. #   include "hanconv.h"
  68. #endif
  69. #if HDEBUG
  70. #   include "syslog.h"
  71. #else
  72. #   define    debug(x)
  73. #endif
  74.  
  75. Prototype int GetFat(void);
  76. Prototype void FreeFat(void);
  77. Prototype word GetFatEntry(word cluster);
  78. Prototype void SetFatEntry(word cluster, word value);
  79. Prototype word FindFreeCluster(word prev);
  80. Prototype word ExtendClusterChain(word cluster);
  81. Prototype void FreeClusterChain(word cluster);
  82. Prototype struct MSFileHandle *MakeMSFileHandle(struct MSFileLock *fl, long mode);
  83. Prototype struct MSFileHandle *MSOpen(struct MSFileLock *parentdir, char *name, long mode);
  84. Prototype long MSClose(struct MSFileHandle *fh);
  85. Prototype long FilePos(struct MSFileHandle *fh, long position, long mode);
  86. Prototype void AdjustSeekPos(struct MSFileHandle *fh);
  87. Prototype long MSSeek(struct MSFileHandle *fh, long position, long mode);
  88. Prototype long MSRead(struct MSFileHandle *fh, byte *userbuffer, long size);
  89. Prototype long MSWrite(struct MSFileHandle *fh, byte *userbuffer, long size);
  90. Prototype long MSDeleteFile(struct MSFileLock *parentdir, byte *name);
  91. Prototype long MSSetDate(struct MSFileLock *parentdir, byte *name, struct DateStamp *datestamp);
  92. Prototype struct MSFileLock *MSCreateDir(struct MSFileLock *parentdir, byte *name);
  93. Prototype long MSRename(struct MSFileLock *slock, byte *sname, struct MSFileLock *dlock, byte *dname);
  94.  
  95. /*
  96.  * Read the FAT from the disk, and count the free clusters.
  97.  */
  98.  
  99. int
  100. GetFat()
  101. {
  102.     int         i;
  103.     byte       *secptr;
  104.  
  105.     if (!Fat && !(Fat = AllocMem((long) Disk.bps * Disk.spf, BufMemType))) {
  106.     debug(("No memory for FAT\n"));
  107.     return 0;
  108.     }
  109.     FatDirty = FALSE;
  110.     for (i = 0; i < Disk.spf; i++) {
  111.     if (secptr = ReadSec(Disk.res + i)) {
  112.         CopyMem(secptr, Fat + i * Disk.bps, (long) Disk.bps);
  113.         FreeSec(secptr);
  114.     } else {
  115.         /* q&d way to set the entire FAT to FAT_EOF */
  116.         setmem(Fat + i * Disk.bps, (int) Disk.bps, FAT_EOF);    /* 0xFF */
  117.     }
  118.     }
  119.  
  120.     debug(("counting free clusters\n"));
  121.  
  122.     Disk.freeclusts = 0;
  123.     for (i = MS_FIRSTCLUST; i <= Disk.maxclst; i++) {
  124.     if (GetFatEntry((word) i) == FAT_UNUSED)
  125.         Disk.freeclusts++;
  126.     }
  127.  
  128.     return 1;
  129. }
  130.  
  131. void
  132. FreeFat()
  133. {
  134.     debug(("FreeFat()\n"));
  135.     if (Fat) {
  136.     FreeMem(Fat, (long) Disk.bps * Disk.spf);
  137.     Fat = NULL;
  138.     FatDirty = FALSE;
  139.     }
  140. }
  141.  
  142. /*-
  143.  *  The FAT consists of 12-bits entries for each cluster,
  144.  *  indicating the next cluster in the chain, or FFF for EOF.
  145.  *
  146.  *  Every two entries are packed in three bytes, like this:
  147.  *
  148.  *  Two entries     abc  123 (for one cluster and the next)
  149.  *  are packed as    bc 3a 12
  150. -*/
  151.  
  152. word
  153. GetFatEntry(cluster)
  154. word        cluster;
  155. {
  156.     if (!Fat && !GetFat())
  157.     return FAT_EOF;
  158.  
  159.     if (Disk.fat16bits) {
  160.     return OtherEndianWord(((word *)Fat)[cluster]);
  161.     } else {
  162.     int    offset = 3 * (cluster / 2);
  163.     word    twoentries;
  164.  
  165.     if (cluster & 1) {
  166.         twoentries = Fat[offset + 1] >> 4;
  167.         twoentries |= Fat[offset + 2] << 4;
  168.     } else {
  169.         twoentries = Fat[offset];
  170.         twoentries |= (Fat[offset + 1] & 0x0F) << 8;
  171.     }
  172.  
  173.     /*
  174.      * Convert the special values 0xFF7 .. 0xFFF to 16 bits so they
  175.      * can be checked consistently.
  176.      */
  177.     if (twoentries >= 0xFF7)
  178.         twoentries |= 0xF000;
  179.  
  180.     return twoentries;
  181.     }
  182. }
  183.  
  184. #if ! READONLY
  185.  
  186. void
  187. SetFatEntry(cluster, value)
  188. word        cluster;
  189. word        value;
  190. {
  191.     if (!Fat && !GetFat())
  192.     return;
  193.  
  194.     if (Disk.fat16bits) {
  195.     ((word *)Fat)[cluster] = OtherEndianWord(value);
  196.     } else {
  197.     int    offset = 3 * (cluster / 2);
  198.  
  199.     if (cluster & 1) {        /* 123 kind of entry */
  200.         Fat[offset + 2] = value >> 4;
  201.         Fat[offset + 1] &= 0x0F;
  202.         Fat[offset + 1] |= (value & 0x0F) << 4;
  203.     } else {            /* abc kind of entry */
  204.         Fat[offset + 0] = value;
  205.         Fat[offset + 1] &= 0xF0;
  206.         Fat[offset + 1] |= (value >> 8) & 0x0F;
  207.     }
  208.     }
  209.  
  210.     FatDirty = TRUE;
  211.     StartTimer(4);
  212. }
  213.  
  214. /*
  215.  * Find a free cluster to install as the one following this one. Start
  216.  * looking for it right after the given one, so we allocate the cluster
  217.  * chain as contiguous as possible. If we run off the end of the disk, we
  218.  * start again at the beginning. The termination test should not be
  219.  * necessary (and won't work if we are given MSFIRSTCLUST - 1) but won't
  220.  * harm either.
  221.  */
  222.  
  223. word
  224. FindFreeCluster(prev)
  225. word        prev;
  226. {
  227.     word   i;
  228.  
  229.     if (prev == 0 || prev == FAT_EOF)
  230.     prev = MS_FIRSTCLUST - 1;
  231.  
  232.     if (Disk.freeclusts > 0) {
  233.     for (i = prev + 1; i != prev; i++) {
  234.         if (i > Disk.maxclst)    /* Wrap around */
  235.         i = MS_FIRSTCLUST;
  236.         if (GetFatEntry(i) == FAT_UNUSED) {
  237.         SetFatEntry(i, FAT_EOF);
  238.         if (prev >= MS_FIRSTCLUST)
  239.             SetFatEntry(prev, i);
  240.         Disk.freeclusts--;
  241.         return i;
  242.         }
  243.     }
  244.     }
  245.     return FAT_EOF;
  246. }
  247.  
  248. /*
  249.  * Add a cluster to a cluster chain. For input, we get some cluster we
  250.  * know that is on the chain, even if it is the first one.
  251.  */
  252.  
  253. word
  254. ExtendClusterChain(cluster)
  255. word    cluster;
  256. {
  257.     word   nextcluster;
  258.  
  259.     /*
  260.      * Find the end of the cluster chain to tack the new cluster on to.
  261.      * Then FindFreeCluster will (or won't) extend the chain for us.
  262.      */
  263.     if (cluster != 0) {
  264.     while ((nextcluster = NextCluster(cluster)) != FAT_EOF) {
  265.         cluster = nextcluster;
  266.     }
  267.     }
  268.  
  269.     return FindFreeCluster(cluster);
  270. }
  271.  
  272. /*
  273.  * Free a chain of clusters by setting their FAT entries to FAT_UNUSED.
  274.  */
  275.  
  276. void
  277. FreeClusterChain(cluster)
  278. word    cluster;
  279. {
  280.     word   nextcluster;
  281.  
  282.     while (cluster != FAT_EOF) {
  283.     nextcluster = NextCluster(cluster);
  284.     SetFatEntry(cluster, FAT_UNUSED);
  285.     Disk.freeclusts++;
  286.     cluster = nextcluster;
  287.     }
  288. }
  289.  
  290. #endif                /* READONLY */
  291.  
  292. struct MSFileHandle *
  293. MakeMSFileHandle(fl, mode)
  294. struct MSFileLock *fl;
  295. long        mode;
  296. {
  297.     struct MSFileHandle *fh;
  298.  
  299.     if (fl->msfl_Msd.msd_Attributes & ATTR_DIR) {
  300.     error = ERROR_OBJECT_WRONG_TYPE;
  301.     fh = NULL;
  302.     } else if (fh = AllocMem((long) sizeof (*fh), MEMF_PUBLIC)) {
  303. #if ! READONLY
  304.     /* Do we need to truncate the file? */
  305.     if ((mode == MODE_NEWFILE) && fl->msfl_Msd.msd_Cluster) {
  306.         FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  307.         fl->msfl_Msd.msd_Cluster = 0;
  308.         fl->msfl_Msd.msd_Filesize = 0;
  309.         UpdateFileLock(fl);
  310.     }
  311. #endif
  312.     fh->msfh_Cluster = fl->msfl_Msd.msd_Cluster;
  313.     fh->msfh_SeekPos = 0;
  314.     fh->msfh_FileLock = fl;
  315. #if CONVERSIONS
  316.     fh->msfh_Conversion = ConvNone;
  317. #endif
  318.     } else {
  319.     error = ERROR_NO_FREE_STORE;
  320.     }
  321.     return fh;
  322. }
  323.  
  324. /*
  325.  * This routine opens a file.
  326.  */
  327.  
  328. struct MSFileHandle *
  329. MSOpen(parentdir, name, mode)
  330. struct MSFileLock *parentdir;
  331. char           *name;
  332. long        mode;
  333. {
  334.     struct MSFileLock *fl;
  335.     struct MSFileHandle *fh = NULL;
  336.     long        lockmode;
  337.  
  338.     switch (mode) {
  339.     case MODE_NEWFILE:
  340.     case MODE_READWRITE:
  341.     lockmode = EXCLUSIVE_LOCK ^ MODE_CREATEFILE;
  342.     break;
  343.     default:
  344.     mode = MODE_OLDFILE;
  345.     case MODE_OLDFILE:
  346.     lockmode = SHARED_LOCK;
  347.     }
  348.  
  349. #if CONVERSIONS
  350.     ConversionImbeddedInFileName = DefaultConversion;
  351. #endif
  352.     if (fl = MSLock(parentdir, name, lockmode)) {
  353. makefh:
  354.     fh = MakeMSFileHandle(fl, mode);
  355.     if (fh) {
  356. #if CONVERSIONS
  357.         fh->msfh_Conversion = ConversionImbeddedInFileName;
  358. #endif
  359.     } else {
  360.         MSUnLock(fl);
  361.     }
  362.  
  363.     return fh;
  364.     }
  365. #if ! READONLY
  366.     /*
  367.      * If the file was not found, see if we can make a new one. Therefore
  368.      * we need to have an empty spot in the desired directory, and create
  369.      * an MSFileLock for it.
  370.      */
  371.  
  372.     if (!(lockmode & MODE_CREATEFILE) && (fl = EmptyFileLock)) {
  373.     debug(("Creating new file\n"));
  374.     EmptyFileLock = NULL;
  375.     fl->msfl_Msd.msd_Attributes = ATTR_ARCHIVED;
  376.     UpdateFileLock(fl);
  377. #if ! CREATIONDATE_ONLY
  378.     UpdateFileLock(fl->msfl_Parent);
  379. #endif
  380.  
  381.     goto makefh;
  382.     }
  383.     if (EmptyFileLock) {
  384.     MSUnLock(EmptyFileLock);
  385.     EmptyFileLock = NULL;
  386.     }
  387. #endif
  388.  
  389.     return NULL;
  390. }
  391.  
  392. long
  393. MSClose(fh)
  394. struct MSFileHandle *fh;
  395. {
  396.     if (fh) {
  397.     MSUnLock(fh->msfh_FileLock);
  398.     FreeMem(fh, (long) sizeof (*fh));
  399.     }
  400.     return DOSTRUE;
  401. }
  402.  
  403. long
  404. FilePos(fh, position, mode)
  405. struct MSFileHandle *fh;
  406. long        position;
  407. long        mode;
  408. {
  409.     switch (mode) {
  410.     default:
  411.     case OFFSET_BEGINNING:
  412.     return position;
  413.     case OFFSET_CURRENT:
  414.     return fh->msfh_SeekPos + position;
  415.     break;
  416.     case OFFSET_END:
  417.     return fh->msfh_FileLock->msfl_Msd.msd_Filesize + position;
  418.     }
  419. }
  420.  
  421. #if defined(ACTION_SET_FILE_SIZE)
  422. void
  423. AdjustSeekPos(fh)
  424. struct MSFileHandle *fh;
  425. {
  426.     if (fh->msfh_SeekPos > fh->msfh_FileLock->msfl_Msd.msd_Filesize) {
  427.     /* fh->msfh_Cluster needs to be fully recalculated */
  428.     MSSeek(fh, 0, OFFSET_BEGINNING);
  429.     MSSeek(fh, 0, OFFSET_END);
  430.     }
  431. }
  432. #endif
  433.  
  434. long
  435. MSSeek(fh, position, mode)
  436. struct MSFileHandle *fh;
  437. long        position;
  438. long        mode;
  439. {
  440.     long        oldpos;
  441.     long        newpos;
  442.     long        filesize = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
  443.     word        cluster = fh->msfh_Cluster;
  444.     word        oldcluster;
  445.     word        newcluster;
  446.  
  447. #if defined(ACTION_SET_FILE_SIZE)
  448.     if (fh->msfh_SeekPos > fh->msfh_FileLock->msfl_Msd.msd_Filesize) {
  449.     fh->msfh_SeekPos = fh->msfh_FileLock->msfl_Msd.msd_Filesize;
  450.     }
  451. #endif
  452.     oldpos = fh->msfh_SeekPos;
  453.     newpos = FilePos(fh, position, mode);
  454.  
  455.     if (newpos < 0 || newpos > filesize) {
  456.     error = ERROR_SEEK_ERROR;
  457.     return -1;
  458.     }
  459.     newcluster = newpos / Disk.bpc;
  460.     oldcluster = oldpos / Disk.bpc;
  461.     debug(("MSSeek: pos %ld mode %ld oldcluster %ld newcluster %ld\n", 
  462.     position, mode, oldcluster, newcluster));
  463.  
  464.     if (oldcluster > newcluster) {    /* Seek backwards */
  465.     cluster = fh->msfh_FileLock->msfl_Msd.msd_Cluster;
  466.     oldcluster = 0;
  467.     debug(("rewind: cluster %ld\n", cluster));
  468.     }
  469.     if (oldcluster < newcluster) {
  470.     if (CheckLock(fh->msfh_FileLock))
  471.         return -1;
  472.     while (oldcluster < newcluster) {
  473.         cluster = NextCluster(cluster);
  474.         oldcluster++;
  475.         debug(("forward: %ldth cluster %ld\n", oldcluster, cluster));
  476.     }
  477.     }
  478.     fh->msfh_Cluster = cluster;
  479.     fh->msfh_SeekPos = newpos;
  480.     debug(("MSSeek: newpos %ld cluster %ld\n", newpos, cluster));
  481.  
  482.     return oldpos;
  483. }
  484.  
  485. long
  486. MSRead(fh, userbuffer, size)
  487. struct MSFileHandle *fh;
  488. byte  *userbuffer;
  489. long    size;
  490. {
  491.     long        oldsize;
  492.  
  493.     if (CheckLock(fh->msfh_FileLock))
  494.     return -1;
  495.  
  496. #if defined(ACTION_SET_FILE_SIZE)
  497.     AdjustSeekPos(fh);
  498. #endif
  499.     if (fh->msfh_SeekPos + size > fh->msfh_FileLock->msfl_Msd.msd_Filesize)
  500.     size = fh->msfh_FileLock->msfl_Msd.msd_Filesize - fh->msfh_SeekPos;
  501.  
  502.     oldsize = size;
  503.  
  504.     while (size > 0) {
  505.     word        offset;
  506.     word        sector;
  507.     byte           *diskbuffer;
  508.     long        tocopy;
  509.  
  510.     offset = fh->msfh_SeekPos % Disk.bpc;
  511.     sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  512.     if (diskbuffer = ReadSec(sector)) {
  513.         offset %= Disk.bps;
  514.         tocopy = lmin(size, Disk.bps - offset);
  515.  
  516. #if CONVERSIONS
  517.         (rd_Conv[fh->msfh_Conversion])(diskbuffer + offset, userbuffer,
  518.                        tocopy);
  519. #else
  520.         CopyMem(diskbuffer + offset, userbuffer, tocopy);
  521. #endif
  522.         userbuffer += tocopy;
  523.         size -= tocopy;
  524.         FreeSec(diskbuffer);
  525.         /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  526.         fh->msfh_SeekPos += tocopy;
  527.         if (fh->msfh_SeekPos % Disk.bpc == 0)
  528.         fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  529.     } else {        /* Read error. Return amount successfully
  530.                  * read, if any. Else return -1 for error. */
  531.         if (size == oldsize) {
  532.         return -1;
  533.         }
  534.         return oldsize - size;
  535.     }
  536.     }
  537.  
  538.     debug(("MSRead: SeekPos %ld\n", fh->msfh_SeekPos));
  539.     return oldsize;
  540. }
  541.  
  542. long
  543. MSWrite(fh, userbuffer, size)
  544. struct MSFileHandle *fh;
  545. byte  *userbuffer;
  546. long    size;
  547. {
  548. #if READONLY
  549.     return -1;
  550. #else
  551.     long        oldsize;
  552.     struct MSFileLock *fl = fh->msfh_FileLock;
  553.     word        prevclust = fl->msfl_Msd.msd_Cluster;
  554.     word        update = 0;
  555.  
  556.     if (CheckLock(fl))
  557.     return -1;
  558.  
  559.     if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  560.     error = ERROR_WRITE_PROTECTED;
  561.     return -1;
  562.     }
  563.  
  564. #if defined(ACTION_SET_FILE_SIZE)
  565.     AdjustSeekPos(fh);
  566. #endif
  567.     oldsize = size;
  568.     /* Check if this will fit on the disk */
  569.     {
  570.     long        new;
  571.     long        old;
  572.  
  573.     new = (fh->msfh_SeekPos + size + Disk.bpc - 1) / Disk.bpc;
  574.     old = (fl->msfl_Msd.msd_Filesize + Disk.bpc - 1) / Disk.bpc;
  575.  
  576.     if (new - old > (long)Disk.freeclusts) {
  577.         error = ERROR_DISK_FULL;
  578.         goto some_error;
  579.     }
  580.     }
  581.  
  582.     while (size > 0) {
  583.     /*
  584.      * Do we need to extend the file?
  585.      */
  586.  
  587.     if (fh->msfh_Cluster == 0 || fh->msfh_Cluster == FAT_EOF) {
  588.         word        newclust;
  589.  
  590.         newclust = ExtendClusterChain(prevclust);
  591.         debug(("Extend with %ld\n", (long)newclust));
  592.         if (newclust != FAT_EOF) {
  593.         if (prevclust == 0) {    /* Record first cluster in dir */
  594.             fl->msfl_Msd.msd_Cluster = newclust;
  595.         }
  596.         fh->msfh_Cluster = newclust;
  597.         prevclust = newclust;
  598.         } else {
  599.         /* "Can't happen" */
  600.         error = ERROR_DISK_FULL;
  601.         goto some_error;
  602.         }
  603.     }
  604.     {
  605.         word        offset;
  606.         word        sector;
  607.         byte       *diskbuffer;
  608.         long        tocopy;
  609.  
  610.         offset = fh->msfh_SeekPos % Disk.bpc;
  611.         sector = ClusterOffsetToSector(fh->msfh_Cluster, (word) offset);
  612.         offset %= Disk.bps;
  613.         tocopy = lmin(size, Disk.bps - offset);
  614.  
  615.         if (offset == 0 && fh->msfh_SeekPos >= fl->msfl_Msd.msd_Filesize)
  616.         diskbuffer = EmptySec(sector);
  617.         else
  618.         diskbuffer = ReadSec(sector);
  619.  
  620.         if (diskbuffer != NULL) {
  621. #if CONVERSIONS
  622.         (wr_Conv[fh->msfh_Conversion])(userbuffer, diskbuffer + offset, tocopy);
  623. #else
  624.         CopyMem(userbuffer, diskbuffer + offset, tocopy);
  625. #endif
  626.         userbuffer += tocopy;
  627.         size -= tocopy;
  628.         MarkSecDirty(diskbuffer);
  629.         FreeSec(diskbuffer);
  630.         /* MSSeek(fh, tocopy, (long) OFFSET_CURRENT); */
  631.         fh->msfh_SeekPos += tocopy;
  632.         if (fh->msfh_SeekPos % Disk.bpc == 0)
  633.             fh->msfh_Cluster = NextCluster(fh->msfh_Cluster);
  634.         if (fh->msfh_SeekPos > fl->msfl_Msd.msd_Filesize)
  635.             fl->msfl_Msd.msd_Filesize = fh->msfh_SeekPos;
  636.         fl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
  637.         update = 1;
  638.         } else {        /* Write error. */
  639.     some_error:
  640.         if (update) {
  641. #if CREATIONDATE_ONLY
  642.             DirtyFileLock(fl);
  643. #else
  644.             UpdateFileLock(fl);
  645. #endif
  646.         }
  647. #if 1
  648.         return -1;    /* We lose the information about how much
  649.                  * data we wrote, but the standard file system
  650.                  * seems to do it this way. */
  651. #else
  652.         if (size == oldsize) {
  653.             return -1;
  654.         }
  655.         return oldsize - size;    /* Amount successfully written */
  656. #endif
  657.         }
  658.     }
  659.     }
  660.  
  661.     if (update) {
  662. #if CREATIONDATE_ONLY
  663.     DirtyFileLock(fl);
  664. #else
  665.     UpdateFileLock(fl);
  666. #endif
  667.     }
  668.     debug(("MSWrite: SeekPos %ld\n", fh->msfh_SeekPos));
  669.  
  670.     return oldsize;
  671. #endif
  672. }
  673.  
  674. long
  675. MSDeleteFile(parentdir, name)
  676. struct MSFileLock *parentdir;
  677. byte           *name;
  678. {
  679. #if READONLY
  680.     return DOSFALSE;
  681. #else
  682.     struct MSFileLock *fl;
  683.  
  684.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  685.     if (fl) {
  686.     if (fl->msfl_Msd.msd_Attributes & ATTR_READONLY) {
  687.         error = ERROR_DELETE_PROTECTED;
  688.         goto some_error;
  689.     }
  690.     if (fl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY) {
  691.         struct FileInfoBlock fib;
  692.  
  693.         /*
  694.          * We normally can't get REAL exclusive locks on directories,
  695.          * so we check here just to be sure. We don't want to delete
  696.          * anyone's current directory, do we?
  697.          */
  698.  
  699.         if (fl->msfl_Refcount != 1 || fl == RootLock) {
  700.         error = ERROR_OBJECT_IN_USE;
  701.         goto some_error;
  702.         }
  703.         if (MSExamine(fl, &fib) &&    /* directory itself */
  704.         MSExNext(fl, &fib)) {    /* should fail */
  705.         if (error == 0) {
  706.         not_empty:
  707.             error = ERROR_DIRECTORY_NOT_EMPTY;
  708.         some_error:
  709.             MSUnLock(fl);
  710.             return DOSFALSE;
  711.         }
  712.         }
  713.         if (error != ERROR_NO_MORE_ENTRIES)
  714.         goto some_error;
  715.  
  716.         error = 0;
  717.     }
  718.     if (fl->msfl_Msd.msd_Cluster)
  719.         FreeClusterChain(fl->msfl_Msd.msd_Cluster);
  720.     fl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  721.     WriteFileLock(fl);
  722. #if ! CREATIONDATE_ONLY
  723.     UpdateFileLock(fl->msfl_Parent);
  724. #endif
  725.     MSUnLock(fl);
  726.  
  727.     return DOSTRUE;
  728.     }
  729.     return DOSFALSE;
  730. #endif
  731. }
  732.  
  733. long
  734. MSSetDate(parentdir, name, datestamp)
  735. struct MSFileLock *parentdir;
  736. byte           *name;
  737. struct DateStamp *datestamp;
  738. {
  739. #if READONLY
  740.     return DOSFALSE;
  741. #else
  742.     struct MSFileLock *fl;
  743.  
  744.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK);
  745.     if (fl) {
  746.     ToMSDate(&fl->msfl_Msd.msd_Date, &fl->msfl_Msd.msd_Time, datestamp);
  747.     WriteFileLock(fl);
  748.     MSUnLock(fl);
  749.  
  750.     return DOSTRUE;
  751.     }
  752.     return DOSFALSE;
  753. #endif
  754. }
  755.  
  756. /*
  757.  * Create a new directory, with its own initial "." and ".." entries.
  758.  */
  759.  
  760. struct MSFileLock *
  761. MSCreateDir(parentdir, name)
  762. struct MSFileLock *parentdir;
  763. byte           *name;
  764. {
  765. #if READONLY
  766.     return DOSFALSE;
  767. #else
  768.     struct MSFileLock *fl;
  769.  
  770.     /*
  771.      * Go create a new file. If we fail later, we have an empty file that
  772.      * we delete again.
  773.      */
  774.  
  775.     fl = MSLock(parentdir, name, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  776.     if (fl || error == ERROR_OBJECT_IN_USE) {
  777.     error = ERROR_OBJECT_EXISTS;
  778.     goto some_error;
  779.     }
  780.     if (error != 0) {
  781.     goto some_error;
  782.     }
  783.     if (fl = EmptyFileLock) {
  784.     debug(("Creating new dir\n"));
  785.     EmptyFileLock = NULL;
  786.     if ((fl->msfl_Msd.msd_Cluster = FindFreeCluster(FAT_EOF)) != FAT_EOF) {
  787.         struct MsDirEntry direntry;
  788.         byte       *sec;
  789.         word        sector;
  790.  
  791.         sector = ClusterToSector(fl->msfl_Msd.msd_Cluster);
  792.         sec = EmptySec(sector);
  793.         if (sec == NULL)
  794.         goto error_no_free_store;
  795.         setmem(sec, (int) Disk.bps, 0);
  796.  
  797.         /*
  798.          * Turn the file into a directory.
  799.          */
  800.         fl->msfl_Msd.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
  801.         fl->msfl_Refcount = 1;    /* Make it non-exclusive */
  802.         UpdateFileLock(fl);
  803.  
  804.         /*
  805.          * Create the "." entry.
  806.          */
  807.         direntry = fl->msfl_Msd;
  808.         strncpy(direntry.msd_Name, DotDot + 1, L_8 + L_3);
  809.         OtherEndianMsd(&direntry);
  810.         ((struct MsDirEntry *) sec)[0] = direntry;
  811.  
  812.         /*
  813.          * Get the real parent directory because we will duplicate the
  814.          * directory entry in the subdirectory.
  815.          */
  816.  
  817.         parentdir = MSParentDir(fl);
  818.         if (parentdir == NULL)    /* Cannot happen */
  819.         parentdir = MSDupLock(RootLock);
  820. #if ! CREATIONDATE_ONLY
  821.         UpdateFileLock(parentdir);
  822. #endif
  823.  
  824.         /*
  825.          * Create the ".." entry.
  826.          */
  827.         direntry = parentdir->msfl_Msd;
  828.         strncpy(direntry.msd_Name, DotDot, L_8 + L_3);
  829.         direntry.msd_Attributes = ATTR_DIRECTORY | ATTR_ARCHIVED;
  830.         OtherEndianMsd(&direntry);
  831.         ((struct MsDirEntry *) sec)[1] = direntry;
  832.  
  833.         MSUnLock(parentdir);
  834.  
  835.         MarkSecDirty(sec);
  836.         FreeSec(sec);
  837.  
  838.         /*
  839.          * Clear out the rest of the newly created directory.
  840.          */
  841.  
  842.         while ((sector = NextClusteredSector(sector)) != SEC_EOF) {
  843.         sec = EmptySec(sector);
  844.         if (sec == NULL)
  845.             goto error_no_free_store;
  846.         setmem(sec, (int) Disk.bps, 0);
  847.         MarkSecDirty(sec);
  848.         FreeSec(sec);
  849.         }
  850.     } else {
  851.         MSUnLock(fl);
  852.         fl = NULL;
  853.         MSDeleteFile(parentdir, name);
  854.         error = ERROR_DISK_FULL;
  855.     }
  856.     }
  857.     if (EmptyFileLock) {
  858.     MSUnLock(EmptyFileLock);
  859.     EmptyFileLock = NULL;
  860.     }
  861.     return fl;
  862.  
  863. error_no_free_store:
  864.     error = ERROR_NO_FREE_STORE;
  865. some_error:
  866.     if (fl)
  867.     MSUnLock(fl);
  868.     return DOSFALSE;
  869. #endif
  870. }
  871.  
  872. /*
  873.  * Rename a file or directory, possibly moving it to a different
  874.  * directory.
  875.  *
  876.  * "Tuned" to also work in full directories by first deleting the source
  877.  * name, then look for a slot to put the destination name. If that fails,
  878.  * we undo the deletion. By playing with the cache, we even avoid a write
  879.  * of the sector with the undeleted entry.
  880.  */
  881.  
  882. long
  883. MSRename(slock, sname, dlock, dname)
  884. struct MSFileLock *slock;
  885. byte           *sname;
  886. struct MSFileLock *dlock;
  887. byte           *dname;
  888. {
  889. #if READONLY
  890.     return DOSFALSE;
  891. #else
  892.     struct MSFileLock *sfl;
  893.     struct MSFileLock *dfl;
  894.     long        success;
  895.     struct CacheSec *scache;
  896.     ulong        oldstatus;
  897.  
  898.     success = DOSFALSE;
  899.     scache = NULL;
  900.     dfl = NULL;
  901.  
  902.     sfl = MSLock(slock, sname, SHARED_LOCK);
  903.     if (sfl == NULL || sfl == RootLock)
  904.     goto some_error;
  905.  
  906.     /*
  907.      * Now we are going to pull a dirty trick with the cache. We are going
  908.      * to temporarily delete the source file, in the chache only, and
  909.      * undelete it again if we cannot create the new name. And above all
  910.      * we want to avoid unnecessary writes if we decide not to do the
  911.      * deletion after all.
  912.      */
  913.     {
  914.     byte           *sec;
  915.     byte        old;
  916.  
  917.     if ((sec = ReadSec(sfl->msfl_DirSector)) == NULL)
  918.         goto some_error;
  919.     scache = FindSecByBuffer(sec);
  920.     oldstatus = scache->sec_Refcount;
  921.  
  922.     old = sfl->msfl_Msd.msd_Name[0];
  923.     sfl->msfl_Msd.msd_Name[0] = DIR_DELETED;
  924.     WriteFileLock(sfl);
  925.     sfl->msfl_Msd.msd_Name[0] = old;
  926.  
  927.     /*
  928.      * Don't FreeSec it yet; we don't want it written out to disk.
  929.      */
  930.     }
  931.  
  932.     /*
  933.      * Now we have freed the directory entry of the source name, we might
  934.      * be able to use it for the destination name. But only if we also
  935.      * temporarily hide the MSFileLock on that spot. Gross hack ahead!
  936.      */
  937.  
  938.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  939.     dfl = MSLock(dlock, dname, EXCLUSIVE_LOCK ^ MODE_CREATEFILE);
  940.     sfl->msfl_DirOffset = ~sfl->msfl_DirOffset;
  941.  
  942.     if (dfl != NULL || error == ERROR_OBJECT_IN_USE) {
  943.     error = ERROR_OBJECT_EXISTS;
  944.     goto undelete;
  945.     }
  946.     dfl = EmptyFileLock;
  947.     EmptyFileLock = NULL;
  948.     if (dfl == NULL) {
  949.     /*
  950.      * Sigh, we could not create the new name. But because of that, we
  951.      * are sure that we need to write nothing to the disk at all. So
  952.      * we can safely reset the sector-dirty flag to what it was
  953.      * before, if we also restore the cached sector.
  954.      */
  955. undelete:
  956.     WriteFileLock(sfl);
  957.     scache->sec_Refcount = oldstatus;
  958.     goto some_error;
  959.     }
  960.     /*
  961.      * Now, if the moved entry was a directory, and it was moved to a
  962.      * different directory, we need to adapt its "..", which is the second
  963.      * entry.
  964.      */
  965.  
  966.     if (sfl->msfl_Msd.msd_Attributes & ATTR_DIRECTORY &&
  967.     sfl->msfl_Parent != dfl->msfl_Parent) {
  968.     struct MSFileLock *parentdir;
  969.     struct MsDirEntry *dir;
  970.  
  971.     if (dir = (struct MsDirEntry *)
  972.         ReadSec(DirClusterToSector(sfl->msfl_Msd.msd_Cluster))) {
  973.         parentdir = MSParentDir(dfl);
  974.         /*
  975.          * Copy everything except the name which must remain "..". But
  976.          * first a quick consistency check...
  977.          */
  978.         debug(("Creating new \"..\"  "));
  979.         if (dir[1].msd_Name[1] == '.') {
  980.         CopyMem(&parentdir->msfl_Msd.msd_Attributes,
  981.             &dir[1].msd_Attributes,
  982.             (long) sizeof (struct MsDirEntry) -
  983.             OFFSETOF(MsDirEntry, msd_Attributes));
  984.         dir[1].msd_Attributes = ATTR_DIRECTORY;
  985.         OtherEndianMsd(&dir[1]);
  986.         MarkSecDirty((byte *)dir);
  987.         }
  988. #if HDEBUG
  989.         else
  990.         debug(("!!! No \"..\" found ??\n"));
  991. #endif
  992.         MSUnLock(parentdir);
  993.         FreeSec((byte *)dir);
  994.     }
  995.     }
  996.     /*
  997.      * Move the name from the new entry to the old filelock. We do this
  998.      * for the case that somebody else has a lock on the (possibly moved)
  999.      * file/directory. Also move the other administration.
  1000.      */
  1001.  
  1002.     strncpy(sfl->msfl_Msd.msd_Name, dfl->msfl_Msd.msd_Name, L_8 + L_3);
  1003.     sfl->msfl_DirSector = dfl->msfl_DirSector;
  1004.     sfl->msfl_DirOffset = dfl->msfl_DirOffset;
  1005.     /*
  1006.      * Free the old, and get the new parent directory. They might be the
  1007.      * same, of course...
  1008.      */
  1009.     MSUnLock(sfl->msfl_Parent);
  1010.     sfl->msfl_Parent = dfl->msfl_Parent;
  1011.     dfl->msfl_Parent = NULL;
  1012.     sfl->msfl_Msd.msd_Attributes |= ATTR_ARCHIVED;
  1013.     WriteFileLock(sfl);     /* Write the new name; the old name
  1014.                  * already has been deleted. */
  1015. #if ! CREATIONDATE_ONLY
  1016.     UpdateFileLock(sfl->msfl_Parent);
  1017.     UpdateFileLock(dfl->msfl_Parent);
  1018. #endif
  1019.     success = DOSTRUE;
  1020.  
  1021. some_error:
  1022.     if (sfl)
  1023.     MSUnLock(sfl);
  1024.     if (dfl)
  1025.     MSUnLock(dfl);
  1026.     if (scache)
  1027.     FreeSec(scache->sec_Data);
  1028.  
  1029.     return success;
  1030. #endif
  1031. }
  1032.